<?php

/**
 * GXRecord
 * Base class for encasing database records.
 * Extend this class for easy enclosed maintenance of records.
 */
class GXRecord extends GXClass {

	// class / table defs
	protected $table;				// the database table name
	protected $node;				// the name of the node (used in show())
	protected $key;				// the unique column name
	protected $sql;				// the sql statement to run to retrieve the record ... needs to include the :key reference

	// helper objects
	public $db		= null;
	// vars
	public $uid		= null;		// the value of this::key
	public $tainted	= false;	// whether data has been changed
	public $data	= null;		// the record for this object (created by get())

	/**
	 * __construct
	 * Record class constructor.
	 * @param integer $id if no id, we'll create a new member record
	 * @param boolean $nocreate we won't create a record, even if we don't have an id
	 * @return object $this
	 */
	public function __construct( $id = null, $nocreate = false ){
		parent::__construct();

		// gxpage classes
		$this->db = $this->GXPage->Database->System;

		// if we have no id, create a record
		if( is_numeric($id) ){
			$this->{$this->key}	= $id;
			$this->uid			= $id;
			$this->data			= $this->get();
		}else{
			// if we're supposed to create a new record, do so
			if( !$nocreate ){
				$this->create();
			}
		}
		return $this;
	}

	/**
	 * __destruct
	 * If there are changes to the internal data for this object, writes them back to the database.
	 * @return boolean
	 */
	public function __destruct(){
		if( $this->tainted ){
			if( !$this->set() ){
				trigger_error( 'Unable to write changes to database', E_USER_ERROR );
				return false;
			}
		}
		return true;
	}

	/************************************************
	 *                 display methods              *
	 ************************************************/

	/**
	 * show
	 * Builds the entire record and returns it as a GXDOM object.
	 * @return object GXDOM
	 */
	public function show(){
		// grab this record
		$info = $this->data;

		// use the key as an attribute, so unset it from the record
		$atts = array(
			$this->key => $this->{$this->key}
			);
		unset($info->{$this->key});
		// create the xml
		$rec = new GXDOM($this->node,$info,$atts);

		// return the entire object
		return $rec;
	}

	/************************************************
	 *              magic record methods            *
	 ************************************************/

	/**
	 * __get
	 * Returns the named parameter from the stored record data.
	 * @param string $name
	 * @return mixed
	 */
	protected function __get( $name = null ){
		// first check for a top-level class var
		if( isset($this->$name) ){
			return $this->$name;
		}
		// next check this::data
		if( isset($this->data->$name) ){
			return $this->data->$name;
		}
		// we didn't find anything
		trigger_error( 'No parameter by that name (' .$name .')', E_USER_WARNING );
		return false;
	}

	/**
	 * __set
	 * Sets the value of the named parameter.
	 * @param string $name
	 * @param mixed $value
	 * @return boolean
	 */
	protected function __set( $name = null, $value = null ){
		$this->tainted = true;
		return $this->data->$name = $value;
	}

	/************************************************
	 *              core member methods             *
	 ************************************************/

	/**
	 * get
	 * Returns the record.
	 * @param integer $id
	 * @return object
	 */
	protected function get( $id = null ){
		if( !$id ){
			if( !$this->{$this->key} ){
				trigger_error( 'Missing ' .$this->key .' and no class ' .$this->key, E_USER_ERROR );
				return false;
			}
			$id = $this->{$this->key};
			$this->{$this->key} = $id;
		}
		$r = null;
		if( !($r = $this->db->get( $this->sql, array( $this->key => $id ) )) ){
			trigger_error( 'Unable to get record', E_USER_ERROR );
			return false;
		}
		// load data into this::uid and this::data
		$this->{$this->key}	= $r[0]->{$this->key};
		$this->uid			= $r[0]->{$this->key};
		$this->data			= $r[0];
		trigger_error( 'found ' .$this->key .'=' .$this->{$this->key} .' for ' .$this->table, E_USER_WARNING );
		return $this->data;
	}

	/**
	 * set
	 * Writes $this::data back to the database for this record.
	 * @param array $info the list of col=>val pairs to update
	 * @param array $exclude a list of column names to not include in the generated sql
	 * @return boolean
	 */
	protected function set( $info = null, $exclude = array() ){
		// if no id, error
		if( !is_numeric($this->{$this->key}) ){
			trigger_error( 'set called, but there is no record', E_USER_ERROR );
			return false;
		}
		// if we have info, only use columns which are part of info
		// otherwise, use everything in this::data
		$info = ($info) ? array_intersect_assoc($info,(array)$this->data) : (array)$this->data;

		// get rid of columns that don't exist in the table
		// sanity checks
		if( !count($info) ){
			trigger_error( 'Missing info', E_USER_ERROR );
			return false;
		}
		// get rid of the table key
		unset($info[$this->key]);

		// init param vars
		$p = null;
		// update the account's info based on the array passed in
		foreach( $info as $k => $v ){
			// if the column name is in the exclude array, don't use it
			if( in_array($k,$exclude) ){
				continue;
			}
			$p['update'][$k] = $k .'=:' .$k;
			$v = get_magic_quotes_gpc() ? stripslashes($v) : $v;
			$p['params'][$k] = $v;
		}
		$sql = 'UPDATE ' .$this->table .' SET ' .implode(',',$p['update']) .' WHERE ' .$this->key .' = ' .$this->{$this->key};
		if( !$this->db->run( $sql, $p['params'] ) ){
			trigger_error( 'Unable to update ' .$this->table .' (' .$this->{$this->key} .')', E_USER_ERROR );
			return false;
		}
		// turn off tainted
		$this->tainted = false;
		return true;
	}

	/**
	 * create
	 * Creates a new member record using the provided data.
	 * @param array $info col=>val array to send to GXDB
	 * @return integer the id of the new record
	 */
	protected function create( $info = null ){
		if( $this->{$this->key} ){
			trigger_error( 'There is already a ' .$this->table .' record for that ' .$this->key .' (' .$this->{$this->key} .')', E_USER_ERROR );
			return false;
		}
		// if no data, database will fail
		if( !$info ){
			trigger_error( 'Missing info ... you must provide at least one column', E_USER_ERROR );
			return false;
		}
		$r = 0;
		if( !($r = $this->db->set( $this->table, $info )) ){
			trigger_error( 'Unable to create the ' .$this->table .' record for ' .print_r($info,true), E_USER_ERROR );
			return false;
		}
		// return this::get so it will load these new values into this object
		return $this->get( $r[0]->{$this->key} );
	}

}
?>